"""
实时面部表情分析GUI应用程序
提供摄像头预览、实时面部检测和表情分析功能
"""
import tkinter as tk
from tkinter import ttk
import cv2
from PIL import Image, ImageTk
import mediapipe as mp
import threading
import time
import logging
import numpy as np
from analysis.facial_analysis_engine import FacialAnalysisEngine
from analysis.expression_analyzer import ExpressionAnalyzer
from config import Config

class FacialExpressionGUI:
    def __init__(self, window):
        self.window = window
        self.window.title("面部表情实时分析系统")
        self.window.geometry("1200x800")
        
        # 初始化MediaPipe
        self.mp_face_mesh = mp.solutions.face_mesh
        self.mp_face_detection = mp.solutions.face_detection
        self.mp_drawing = mp.solutions.drawing_utils
        self.mp_drawing_styles = mp.solutions.drawing_styles
        
        self.face_mesh = self.mp_face_mesh.FaceMesh(
            static_image_mode=False,
            max_num_faces=1,
            refine_landmarks=True,
            min_detection_confidence=0.5,
            min_tracking_confidence=0.5
        )
        
        # 初始化分析引擎和表情分析器
        self.analysis_engine = FacialAnalysisEngine()
        self.expression_analyzer = ExpressionAnalyzer()
        
        # 控制变量
        self.cap = None
        self.is_running = False
        self.show_landmarks = tk.BooleanVar(value=True)  # 控制是否显示面部特征点
        self.current_frame = None
        self.analysis_result = None
        self.last_expressions = {}  # 存储上一次的表情数据，用于平滑显示
        self.face_detected = False  # 标记是否检测到面部
        self.camera_index = 0  # 当前摄像头索引
        
        # 数据存储用于图表
        self.wake_data = []      # 唤醒数据
        self.calibration_data = []  # 校价数据
        self.expression_history = []  # 表情历史数据
        self.mood_history = []    # 心情历史数据
        
        # 表情强度变量
        self.expression_vars = {}
        self.mood_vars = {}
        
        # 图表说明文本
        self.chart_descriptions = {
            "唤醒折线图": "显示随时间变化的唤醒水平，反映用户的兴奋程度",
            "校价折线图": "显示面部校准数据，反映面部肌肉的活动水平",
            "表情折线图": "显示各种基本表情的强度变化趋势",
            "表情强度图": "显示当前各表情的强度，通过柱状图直观展示",
            "表情概要图": "按高兴、悲伤、惊喜等分类显示表情强度的饼图",
            "情绪绕线模型": "按活跃/不活跃、愉快/不愉快分类的情绪分布饼图"
        }
        
        # 创建带滚动条的主框架
        self.create_scrollable_widgets()
        self.window.after(100, self.detect_cameras)  # 延迟检测摄像头以确保界面初始化完成
        
    def create_scrollable_widgets(self):
        """创建带滚动条的界面"""
        # 创建Canvas和滚动条
        self.main_canvas = tk.Canvas(self.window)
        self.scrollbar = ttk.Scrollbar(self.window, orient="vertical", command=self.main_canvas.yview)
        self.scrollable_frame = ttk.Frame(self.main_canvas)
        
        # 配置滚动区域
        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.main_canvas.configure(
                scrollregion=self.main_canvas.bbox("all")
            )
        )
        
        # 在Canvas中创建窗口
        self.main_canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.main_canvas.configure(yscrollcommand=self.scrollbar.set)
        
        # 布局
        self.main_canvas.pack(side="left", fill="both", expand=True)
        self.scrollbar.pack(side="right", fill="y")
        
        # 绑定鼠标滚轮事件
        self.main_canvas.bind("<MouseWheel>", self._on_mousewheel)
        self.scrollable_frame.bind("<MouseWheel>", self._on_mousewheel)
        
        # 创建界面组件
        self.create_widgets_in_scrollable_frame()
        
    def _on_mousewheel(self, event):
        """处理鼠标滚轮事件"""
        self.main_canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        
    def create_widgets_in_scrollable_frame(self):
        """在可滚动框架中创建界面组件"""
        # 主框架
        main_frame = ttk.Frame(self.scrollable_frame)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 顶部区域 - 视频显示和控制
        top_frame = ttk.Frame(main_frame)
        top_frame.pack(side=tk.TOP, fill=tk.X)
        
        # 左侧区域 - 视频显示（缩小尺寸）
        video_frame = ttk.LabelFrame(top_frame, text="视频预览")
        video_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=False)
        video_frame.config(width=300, height=225)  # 缩小视频预览框
        video_frame.pack_propagate(False)  # 防止子元素改变frame大小
        
        # 创建画布用于居中显示视频
        self.video_canvas = tk.Canvas(video_frame, width=300, height=225)
        self.video_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 视频显示标签
        self.video_label = ttk.Label(self.video_canvas)
        
        # 中间区域 - 图表显示（与视频同一行）
        chart_frame = ttk.Frame(top_frame)
        chart_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(10, 0))
        
        # 创建图表区域（表情概要图和情绪绕线模型）
        self.create_top_charts(chart_frame)
        
        # 视频下方的控制按钮区域
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(side=tk.TOP, fill=tk.X, pady=(10, 0))
        
        # 控制按钮
        button_frame = ttk.Frame(control_frame)
        button_frame.pack(side=tk.TOP, fill=tk.X)
        
        # 摄像头控制按钮
        button_row1 = ttk.Frame(button_frame)
        button_row1.pack(fill=tk.X, pady=5)
        
        self.start_button = ttk.Button(button_row1, text="启动摄像头", command=self.start_camera)
        self.start_button.pack(side=tk.LEFT, padx=(0, 5))
        
        self.stop_button = ttk.Button(button_row1, text="停止摄像头", command=self.stop_camera, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=(5, 0))
        
        # 摄像头选择区域
        camera_frame = ttk.Frame(button_frame)
        camera_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(camera_frame, text="摄像头:").pack(side=tk.LEFT)
        self.camera_combo = ttk.Combobox(camera_frame, state="readonly", width=20)
        self.camera_combo.pack(side=tk.LEFT, padx=5)
        self.camera_combo.bind("<<ComboboxSelected>>", self.on_camera_change)
        
        # 可视化控制
        viz_frame = ttk.LabelFrame(button_frame, text="可视化选项")
        viz_frame.pack(fill=tk.X, pady=10)
        
        self.landmark_checkbox = ttk.Checkbutton(
            viz_frame, 
            text="显示面部特征点", 
            variable=self.show_landmarks,
            command=self.toggle_landmarks)
        self.landmark_checkbox.pack(anchor=tk.W, padx=5, pady=2)
        
        # 分析结果显示区域（横向排列）
        result_frame = ttk.LabelFrame(button_frame, text="分析结果")
        result_frame.pack(fill=tk.X, expand=False, pady=10)
        
        # 使用Frame容器横向排列分析结果
        results_container = ttk.Frame(result_frame)
        results_container.pack(fill=tk.X, padx=5, pady=5)
        
        # 表情检测结果
        self.expression_label = ttk.Label(results_container, text="当前表情: 未知")
        self.expression_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 心情识别结果
        self.mood_label = ttk.Label(results_container, text="当前心情: 未知")
        self.mood_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 医疗评估结果
        self.medical_label = ttk.Label(results_container, text="医疗评估: 无异常")
        self.medical_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 对称性比率
        self.symmetry_label = ttk.Label(results_container, text="对称性比率: --")
        self.symmetry_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 面部朝向
        self.orientation_label = ttk.Label(results_container, text="面部朝向: --")
        self.orientation_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 评分
        self.score_label = ttk.Label(results_container, text="评分: --")
        self.score_label.pack(side=tk.LEFT)
        
        # 下方区域 - 其他图表显示
        bottom_chart_frame = ttk.Frame(main_frame)
        bottom_chart_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True, pady=(10, 0))
        
        # 创建底部图表区域（每行2个图表，每个宽度50%）
        self.create_bottom_charts(bottom_chart_frame)
        
        # 状态栏
        self.status_bar = ttk.Label(self.scrollable_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
    def create_top_charts(self, parent):
        """创建顶部图表区域（与视频同一行）"""
        # 创建一个容器来水平排列表情概要图和情绪绕线模型
        top_charts_container = ttk.Frame(parent)
        top_charts_container.pack(fill=tk.BOTH, expand=True)
        
        # 表情概要图
        summary_frame = ttk.Frame(top_charts_container)
        summary_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=(0, 5), padx=(0, 5))
        
        # 添加标题和帮助按钮
        summary_title_frame = ttk.Frame(summary_frame)
        summary_title_frame.pack(fill=tk.X)
        summary_label = ttk.Label(summary_title_frame, text="表情概要图")
        summary_label.pack(side=tk.LEFT)
        self.add_help_button(summary_title_frame, "表情概要图", self.chart_descriptions["表情概要图"])
        
        self.expression_summary_canvas = tk.Canvas(summary_frame, bg="white")
        self.expression_summary_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 情绪绕线模型
        mood_frame = ttk.Frame(top_charts_container)
        mood_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=(0, 5), padx=(5, 0))
        
        # 添加标题和帮助按钮
        mood_title_frame = ttk.Frame(mood_frame)
        mood_title_frame.pack(fill=tk.X)
        mood_label = ttk.Label(mood_title_frame, text="情绪绕线模型")
        mood_label.pack(side=tk.LEFT)
        self.add_help_button(mood_title_frame, "情绪绕线模型", self.chart_descriptions["情绪绕线模型"])
        
        self.mood_pie_canvas = tk.Canvas(mood_frame, bg="white")
        self.mood_pie_canvas.pack(fill=tk.BOTH, expand=True)
        
    def create_bottom_charts(self, parent):
        """创建底部图表区域（每行2个图表）"""
        # 第一行图表
        row1_frame = ttk.Frame(parent)
        row1_frame.pack(fill=tk.X, pady=5)
        
        # 唤醒折线图
        wake_frame = ttk.Frame(row1_frame)
        wake_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
        wake_frame.config(width=parent.winfo_width() // 2)  # 设置宽度为50%
        
        # 添加标题和帮助按钮
        wake_title_frame = ttk.Frame(wake_frame)
        wake_title_frame.pack(fill=tk.X)
        wake_label = ttk.Label(wake_title_frame, text="唤醒折线图")
        wake_label.pack(side=tk.LEFT)
        self.add_help_button(wake_title_frame, "唤醒折线图", self.chart_descriptions["唤醒折线图"])
        
        self.wake_canvas = tk.Canvas(wake_frame, bg="white")
        self.wake_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 校价折线图
        calibration_frame = ttk.Frame(row1_frame)
        calibration_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
        calibration_frame.config(width=parent.winfo_width() // 2)  # 设置宽度为50%
        
        # 添加标题和帮助按钮
        calibration_title_frame = ttk.Frame(calibration_frame)
        calibration_title_frame.pack(fill=tk.X)
        calibration_label = ttk.Label(calibration_title_frame, text="校价折线图")
        calibration_label.pack(side=tk.LEFT)
        self.add_help_button(calibration_title_frame, "校价折线图", self.chart_descriptions["校价折线图"])
        
        self.calibration_canvas = tk.Canvas(calibration_frame, bg="white")
        self.calibration_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 第二行图表
        row2_frame = ttk.Frame(parent)
        row2_frame.pack(fill=tk.X, pady=5)
        
        # 表情折线图
        expression_line_frame = ttk.Frame(row2_frame)
        expression_line_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
        expression_line_frame.config(width=parent.winfo_width() // 2)  # 设置宽度为50%
        
        # 添加标题和帮助按钮
        expression_line_title_frame = ttk.Frame(expression_line_frame)
        expression_line_title_frame.pack(fill=tk.X)
        expression_line_label = ttk.Label(expression_line_title_frame, text="表情折线图")
        expression_line_label.pack(side=tk.LEFT)
        self.add_help_button(expression_line_title_frame, "表情折线图", self.chart_descriptions["表情折线图"])
        
        self.expression_line_canvas = tk.Canvas(expression_line_frame, bg="white")
        self.expression_line_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 表情强度图
        expression_bar_frame = ttk.Frame(row2_frame)
        expression_bar_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
        expression_bar_frame.config(width=parent.winfo_width() // 2)  # 设置宽度为50%
        
        # 添加标题和帮助按钮
        expression_bar_title_frame = ttk.Frame(expression_bar_frame)
        expression_bar_title_frame.pack(fill=tk.X)
        expression_bar_label = ttk.Label(expression_bar_title_frame, text="表情强度图")
        expression_bar_label.pack(side=tk.LEFT)
        self.add_help_button(expression_bar_title_frame, "表情强度图", self.chart_descriptions["表情强度图"])
        
        self.expression_bar_canvas = tk.Canvas(expression_bar_frame, bg="white")
        self.expression_bar_canvas.pack(fill=tk.BOTH, expand=True)
        
    def add_help_button(self, parent, title, description):
        """添加帮助按钮"""
        def show_help(event=None):
            # 创建一个顶层窗口显示帮助信息
            help_window = tk.Toplevel(self.window)
            help_window.title("帮助信息")
            help_window.geometry("300x150")
            help_window.transient(self.window)
            help_window.grab_set()
            
            label = ttk.Label(help_window, text=description, wraplength=280)
            label.pack(padx=10, pady=10)
            
            close_button = ttk.Button(help_window, text="关闭", command=help_window.destroy)
            close_button.pack(pady=10)
            
        # 在标题旁边添加问号按钮
        help_button = ttk.Button(parent, text="?", width=2, command=show_help)
        help_button.pack(side=tk.RIGHT, padx=5, pady=5)
        
    def detect_cameras(self):
        """检测可用的摄像头设备"""
        available_cameras = []
        for i in range(10):  # 检查前10个索引
            cap = cv2.VideoCapture(i)
            if cap.isOpened():
                ret, frame = cap.read()
                if ret:
                    available_cameras.append(f"摄像头 {i}")
                cap.release()
        
        if not available_cameras:
            available_cameras.append("默认摄像头")
            
        self.camera_combo['values'] = available_cameras
        self.camera_combo.current(0)
        
    def on_camera_change(self, event=None):
        """摄像头选择变化事件处理"""
        if self.is_running:
            # 如果正在运行，先停止当前摄像头
            self.stop_camera()
            # 然后重新启动新选择的摄像头
            self.start_camera()
        
    def start_camera(self):
        """启动摄像头"""
        if not self.is_running:
            # 获取选择的摄像头索引
            selected = self.camera_combo.get()
            if selected.startswith("摄像头 "):
                self.camera_index = int(selected.split()[1])
            else:
                self.camera_index = 0
                
            self.cap = cv2.VideoCapture(self.camera_index)
            if not self.cap.isOpened():
                self.status_bar.config(text="错误: 无法打开摄像头")
                return
                
            self.is_running = True
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.status_bar.config(text=f"摄像头 {self.camera_index} 已启动")
            
            # 启动视频处理线程
            self.thread = threading.Thread(target=self.video_loop, daemon=True)
            self.thread.start()
            
    def stop_camera(self):
        """停止摄像头"""
        self.is_running = False
        if self.cap:
            self.cap.release()
            
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        self.status_bar.config(text="摄像头已停止")
        
    def toggle_landmarks(self):
        """切换面部特征点显示"""
        # 这个函数会在复选框状态改变时自动调用
        pass  # 实际的切换在process_frame中处理
        
    def video_loop(self):
        """视频处理循环"""
        # 定义目标显示尺寸
        target_width, target_height = 300, 225
        
        while self.is_running:
            ret, frame = self.cap.read()
            if not ret:
                continue
                
            # 镜像处理
            frame = cv2.flip(frame, 1)
            
            # 处理帧
            self.process_frame(frame)
            
            # 转换为tkinter可显示格式并调整大小
            rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(rgb_frame)
            
            # 按比例缩放图像以适应目标尺寸
            img_width, img_height = img.size
            scale_w = target_width / img_width
            scale_h = target_height / img_height
            scale = min(scale_w, scale_h)  # 选择较小的缩放比例以保持完整图像
            
            # 计算新尺寸
            new_width = int(img_width * scale)
            new_height = int(img_height * scale)
            
            # 缩放图像
            img = img.resize((new_width, new_height), Image.LANCZOS)
            
            imgtk = ImageTk.PhotoImage(image=img)
            
            # 在主线程中更新UI
            self.window.after(0, self.update_video_display, imgtk)
            
    def process_frame(self, frame):
        """处理单帧图像"""
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = self.face_mesh.process(rgb_frame)
        
        # 重置显示数据
        self.reset_display()
        
        if results.multi_face_landmarks:
            self.face_detected = True
            # 根据设置决定是否绘制面部特征点
            if self.show_landmarks.get():
                # 绘制面部特征点
                for face_landmarks in results.multi_face_landmarks:
                    self.mp_drawing.draw_landmarks(
                        image=frame,
                        landmark_list=face_landmarks,
                        connections=self.mp_face_mesh.FACEMESH_TESSELATION,
                        landmark_drawing_spec=None,
                        connection_drawing_spec=self.mp_drawing_styles
                        .get_default_face_mesh_tesselation_style())
                    self.mp_drawing.draw_landmarks(
                        image=frame,
                        landmark_list=face_landmarks,
                        connections=self.mp_face_mesh.FACEMESH_CONTOURS,
                        landmark_drawing_spec=None,
                        connection_drawing_spec=self.mp_drawing_styles
                        .get_default_face_mesh_contours_style())
                    self.mp_drawing.draw_landmarks(
                        image=frame,
                        landmark_list=face_landmarks,
                        connections=self.mp_face_mesh.FACEMESH_IRISES,
                        landmark_drawing_spec=None,
                        connection_drawing_spec=self.mp_drawing_styles
                        .get_default_face_mesh_iris_connections_style())
            
            # 尝试获取更多分析数据
            try:
                # 检查是否包含blendshapes数据
                if hasattr(results, 'face_blendshapes') and results.face_blendshapes:
                    # 使用分析引擎处理面部数据
                    detection_result = results
                    
                    # 分析表情
                    expressions = self.expression_analyzer.analyze_expressions(detection_result)
                    self.update_expression_display(expressions, "基于Blendshapes")
                else:
                    # 如果没有blendshapes数据，使用简化的方法估算表情
                    self.estimate_expressions(results, frame)
                    
            except Exception as e:
                logging.error(f"处理帧时出错: {e}")
        else:
            self.face_detected = False
            # 没有检测到面部时保持显示上一次的数据
            if self.last_expressions:
                self.update_expression_display(self.last_expressions, "未检测到面部[保持]")
                
    def estimate_expressions(self, results, frame):
        """估算表情（当没有blendshapes数据时使用）"""
        try:
            landmarks = results.multi_face_landmarks[0]
            h, w = frame.shape[:2]
            
            # 获取关键点坐标
            # 眉毛点
            left_eyebrow_inner = landmarks.landmark[105]  # 左眉内角
            left_eyebrow_outer = landmarks.landmark[65]   # 左眉外角
            right_eyebrow_inner = landmarks.landmark[334] # 右眉内角
            right_eyebrow_outer = landmarks.landmark[295] # 右眉外角
            nose_top = landmarks.landmark[168]            # 鼻梁顶部
            
            # 眼睛点
            left_eye_top = landmarks.landmark[159]        # 左眼上眼睑
            left_eye_bottom = landmarks.landmark[145]     # 左眼下眼睑
            right_eye_top = landmarks.landmark[386]       # 右眼上眼睑
            right_eye_bottom = landmarks.landmark[374]    # 右眼下眼睑
            
            # 鼻子点
            nose_tip = landmarks.landmark[1]              # 鼻尖
            nose_left = landmarks.landmark[129]           # 左鼻翼
            nose_right = landmarks.landmark[358]          # 右鼻翼
            upper_lip_top = landmarks.landmark[0]         # 上唇中心
            
            # 嘴巴点
            mouth_left = landmarks.landmark[61]           # 嘴左角
            mouth_right = landmarks.landmark[291]         # 嘴右角
            mouth_top = landmarks.landmark[0]             # 上唇中心
            mouth_bottom = landmarks.landmark[17]         # 下唇中心
            mouth_upper_lip = landmarks.landmark[2]       # 上唇突出点
            mouth_lower_lip = landmarks.landmark[13]      # 下唇突出点
            
            # 计算特征值
            # 眉毛抬起程度（通过眉毛点与鼻梁顶部的距离估算）
            left_eyebrow_distance = nose_top.y - left_eyebrow_inner.y
            right_eyebrow_distance = nose_top.y - right_eyebrow_inner.y
            # 基线距离（自然状态下眉毛与鼻子的距离）
            baseline_distance = 0.05
            eyebrow_raise = max(0, min(1, (left_eyebrow_distance + right_eyebrow_distance - 2*baseline_distance) * 10))
            
            # 眼睛闭合程度（通过上下眼睑距离估算）
            left_eye_distance = abs(left_eye_top.y - left_eye_bottom.y)
            right_eye_distance = abs(right_eye_top.y - right_eye_bottom.y)
            # 眼睛完全睁开时距离约为0.04-0.06，闭合时约为0.01
            eye_close = 1 - min(1, max(0, (left_eye_distance + right_eye_distance) * 20))
            
            # 皱鼻程度（通过鼻翼到上唇的距离变化估算）
            left_nose_to_lip = abs(nose_left.y - upper_lip_top.y)
            right_nose_to_lip = abs(nose_right.y - upper_lip_top.y)
            # 基线距离（自然状态下鼻翼到上唇的距离）
            baseline_nose_lip = 0.1
            nose_scrunch = max(0, min(1, (baseline_nose_lip - (left_nose_to_lip + right_nose_to_lip)/2) * 20))
            
            # 微笑程度（通过嘴巴高度估算）
            mouth_height = abs(mouth_top.y - mouth_bottom.y)
            # 基线嘴巴高度（自然状态下）
            baseline_mouth_height = 0.02
            smile = min(1, max(0, (mouth_height - baseline_mouth_height) * 20))
            
            # 撅嘴程度（通过上下唇突出点距离估算）
            lip_pucker_distance = abs(mouth_upper_lip.z - mouth_lower_lip.z)
            # 基线撅嘴距离
            baseline_lip_pucker = 0.01
            lip_pucker = min(1, max(0, (lip_pucker_distance - baseline_lip_pucker) * 50))
            
            # 简化的表情数据
            expressions = {
                'eyebrow_raise': eyebrow_raise,
                'eye_close': eye_close,
                'nose_scrunch': nose_scrunch,
                'smile': smile,
                'lip_pucker': lip_pucker
            }
            
            self.update_expression_display(expressions, "基于特征点估算")
            
        except Exception as e:
            logging.error(f"估算表情时出错: {e}")
                
    def reset_display(self):
        """重置显示数据"""
        # 不重置表情显示，保持上一次的数据
        pass
        
    def update_expression_display(self, expressions, method=""):
        """更新表情显示"""
        # 确定主要表情
        if expressions:
            # 保存当前表情数据
            self.last_expressions = expressions
            
            max_expr = max(expressions.items(), key=lambda x: x[1])
            if max_expr[1] > 0.3:  # 阈值
                expression_names = {
                    'eyebrow_raise': '抬眉',
                    'eye_close': '闭眼',
                    'nose_scrunch': '皱鼻',
                    'smile': '微笑',
                    'lip_pucker': '撅嘴'
                }
                self.expression_label.config(
                    text=f"当前表情: {expression_names.get(max_expr[0], '未知')} ({max_expr[1]:.2f}) {method}")
            else:
                self.expression_label.config(text=f"当前表情: 自然状态 {method}")
                
            # 根据表情推断心情
            self.estimate_mood(expressions)
            
            # 根据表情推断医疗评估
            self.estimate_medical_assessment(expressions)
            
            # 更新对称性比率（模拟数据，添加平滑处理）
            if hasattr(self, '_last_symmetry'):
                # 平滑处理，避免剧烈变化
                new_symmetry = 0.85 + np.random.normal(0, 0.02)
                symmetry_ratio = 0.7 * self._last_symmetry + 0.3 * new_symmetry
            else:
                symmetry_ratio = 0.85 + np.random.normal(0, 0.05)
            symmetry_ratio = max(0.0, min(1.0, symmetry_ratio))  # 限制在0-1之间
            self._last_symmetry = symmetry_ratio
            self.symmetry_label.config(text=f"对称性比率: {symmetry_ratio:.2f}")
            
            # 更新面部朝向（模拟数据，添加平滑处理）
            if hasattr(self, '_last_orientation'):
                # 有一定概率保持上次朝向，避免频繁变化
                if np.random.random() > 0.3:
                    orientation = self._last_orientation
                else:
                    orientations = ["正面", "左偏", "右偏", "抬头", "低头"]
                    orientation = orientations[np.random.randint(0, len(orientations))]
            else:
                orientations = ["正面", "左偏", "右偏", "抬头", "低头"]
                orientation = orientations[np.random.randint(0, len(orientations))]
            self._last_orientation = orientation
            self.orientation_label.config(text=f"面部朝向: {orientation}")
            
            # 更新评分（模拟数据，添加平滑处理）
            if hasattr(self, '_last_score'):
                # 平滑处理，避免剧烈变化
                new_score = 75 + np.random.normal(0, 3)
                score = 0.7 * self._last_score + 0.3 * new_score
            else:
                score = 75 + np.random.normal(0, 10)
            score = max(0.0, min(100.0, score))  # 限制在0-100之间
            self._last_score = score
            self.score_label.config(text=f"评分: {score:.1f}")
            
            # 更新图表数据
            self.update_chart_data(expressions)
        elif self.last_expressions:
            # 如果当前没有检测到面部，但之前有数据，则保持显示
            max_expr = max(self.last_expressions.items(), key=lambda x: x[1])
            if max_expr[1] > 0.3:  # 阈值
                expression_names = {
                    'eyebrow_raise': '抬眉',
                    'eye_close': '闭眼',
                    'nose_scrunch': '皱鼻',
                    'smile': '微笑',
                    'lip_pucker': '撅嘴'
                }
                self.expression_label.config(
                    text=f"当前表情: {expression_names.get(max_expr[0], '未知')} ({max_expr[1]:.2f}) [保持]")
            else:
                self.expression_label.config(text="当前表情: 自然状态 [保持]")
                
            # 根据上一次的表情推断心情
            self.estimate_mood(self.last_expressions)
            
            # 根据上一次的表情推断医疗评估
            self.estimate_medical_assessment(self.last_expressions)
        
    def update_chart_data(self, expressions):
        """更新图表数据"""
        # 添加当前数据到历史记录
        timestamp = time.time()
        
        # 唤醒数据（使用微笑强度作为示例）
        self.wake_data.append((timestamp, expressions.get('smile', 0)))
        if len(self.wake_data) > 50:  # 限制数据点数量，避免图表过于密集
            self.wake_data.pop(0)
            
        # 校价数据（使用抬眉强度作为示例）
        self.calibration_data.append((timestamp, expressions.get('eyebrow_raise', 0)))
        if len(self.calibration_data) > 50:  # 限制数据点数量
            self.calibration_data.pop(0)
            
        # 表情历史数据
        self.expression_history.append((timestamp, expressions))
        if len(self.expression_history) > 50:  # 限制数据点数量
            self.expression_history.pop(0)
            
        # 心情历史数据（使用当前心情作为示例）
        # 这里简化处理，实际应该根据心情计算
        mood_data = {
            'happy': expressions.get('smile', 0),
            'sad': expressions.get('eye_close', 0) * 0.5,
            'angry': (1 - expressions.get('eyebrow_raise', 0)) * 0.7,
            'surprise': expressions.get('eyebrow_raise', 0),
            'fear': expressions.get('eyebrow_raise', 0) * 0.5,
            'disgust': expressions.get('nose_scrunch', 0),
            'pain': expressions.get('eye_close', 0) * 0.3,
            'neutral': 1 - sum([v for v in expressions.values()]) / len(expressions)
        }
        self.mood_history.append((timestamp, mood_data))
        if len(self.mood_history) > 50:  # 限制数据点数量
            self.mood_history.pop(0)
            
        # 更新图表显示
        self.draw_charts()
        
    def draw_charts(self):
        """绘制所有图表"""
        self.draw_wake_chart()
        self.draw_calibration_chart()
        self.draw_expression_line_chart()
        self.draw_expression_bar_chart()
        self.draw_expression_summary_chart()
        self.draw_mood_pie_chart()
        
    def draw_wake_chart(self):
        """绘制唤醒折线图"""
        self.wake_canvas.delete("all")
        if not self.wake_data:
            return
            
        width = self.wake_canvas.winfo_width()
        height = self.wake_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 绘制坐标轴
        margin = 40
        chart_width = width - 2 * margin
        chart_height = height - 2 * margin - 20
        
        # X轴和Y轴
        self.wake_canvas.create_line(margin, margin + chart_height, margin + chart_width, margin + chart_height)  # X轴
        self.wake_canvas.create_line(margin, margin, margin, margin + chart_height)  # Y轴
        
        # 绘制数据点和连线
        if len(self.wake_data) > 1:
            max_val = max([d[1] for d in self.wake_data])
            min_val = min([d[1] for d in self.wake_data])
            val_range = max_val - min_val if max_val != min_val else 1
            
            # 时间范围
            time_range = self.wake_data[-1][0] - self.wake_data[0][0] if len(self.wake_data) > 1 else 1
            
            points = []
            for i, (timestamp, value) in enumerate(self.wake_data):
                x = margin + (timestamp - self.wake_data[0][0]) / time_range * chart_width
                y = margin + chart_height - (value - min_val) / val_range * chart_height
                points.append((x, y))
                
            # 绘制连线
            for i in range(len(points) - 1):
                self.wake_canvas.create_line(points[i], points[i+1], fill="blue", width=2)
                
            # 绘制数据点
            for x, y in points:
                self.wake_canvas.create_oval(x-3, y-3, x+3, y+3, fill="blue")
                
    def draw_calibration_chart(self):
        """绘制校价折线图"""
        self.calibration_canvas.delete("all")
        if not self.calibration_data:
            return
            
        width = self.calibration_canvas.winfo_width()
        height = self.calibration_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 绘制坐标轴
        margin = 40
        chart_width = width - 2 * margin
        chart_height = height - 2 * margin - 20
        
        # X轴和Y轴
        self.calibration_canvas.create_line(margin, margin + chart_height, margin + chart_width, margin + chart_height)  # X轴
        self.calibration_canvas.create_line(margin, margin, margin, margin + chart_height)  # Y轴
        
        # 绘制数据点和连线
        if len(self.calibration_data) > 1:
            max_val = max([d[1] for d in self.calibration_data])
            min_val = min([d[1] for d in self.calibration_data])
            val_range = max_val - min_val if max_val != min_val else 1
            
            # 时间范围
            time_range = self.calibration_data[-1][0] - self.calibration_data[0][0] if len(self.calibration_data) > 1 else 1
            
            points = []
            for i, (timestamp, value) in enumerate(self.calibration_data):
                x = margin + (timestamp - self.calibration_data[0][0]) / time_range * chart_width
                y = margin + chart_height - (value - min_val) / val_range * chart_height
                points.append((x, y))
                
            # 绘制连线
            for i in range(len(points) - 1):
                self.calibration_canvas.create_line(points[i], points[i+1], fill="green", width=2)
                
            # 绘制数据点
            for x, y in points:
                self.calibration_canvas.create_oval(x-3, y-3, x+3, y+3, fill="green")
                
    def draw_expression_line_chart(self):
        """绘制表情折线图"""
        self.expression_line_canvas.delete("all")
        if not self.expression_history:
            return
            
        width = self.expression_line_canvas.winfo_width()
        height = self.expression_line_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 绘制坐标轴
        margin = 50
        chart_width = width - 2 * margin
        chart_height = height - 2 * margin - 20
        
        # X轴和Y轴
        self.expression_line_canvas.create_line(margin, margin + chart_height, margin + chart_width, margin + chart_height)  # X轴
        self.expression_line_canvas.create_line(margin, margin, margin, margin + chart_height)  # Y轴
        
        # 表情类型
        expression_types = ['eyebrow_raise', 'eye_close', 'nose_scrunch', 'smile', 'lip_pucker']
        colors = ['red', 'blue', 'green', 'orange', 'purple']
        
        # 绘制每种表情的折线
        for exp_idx, exp_type in enumerate(expression_types):
            if len(self.expression_history) > 1:
                points = []
                time_range = self.expression_history[-1][0] - self.expression_history[0][0] if len(self.expression_history) > 1 else 1
                
                for i, (timestamp, expressions) in enumerate(self.expression_history):
                    x = margin + (timestamp - self.expression_history[0][0]) / time_range * chart_width
                    y = margin + chart_height - expressions.get(exp_type, 0) * chart_height
                    points.append((x, y))
                    
                # 绘制连线
                for i in range(len(points) - 1):
                    self.expression_line_canvas.create_line(points[i], points[i+1], fill=colors[exp_idx], width=2)
                    
                # 绘制图例
                self.expression_line_canvas.create_rectangle(width - 100, 50 + exp_idx*20, width - 90, 60 + exp_idx*20, fill=colors[exp_idx])
                exp_names = {
                    'eyebrow_raise': '抬眉',
                    'eye_close': '闭眼',
                    'nose_scrunch': '皱鼻',
                    'smile': '微笑',
                    'lip_pucker': '撅嘴'
                }
                self.expression_line_canvas.create_text(width - 85, 55 + exp_idx*20, text=exp_names[exp_type], anchor=tk.W)
                
    def draw_expression_bar_chart(self):
        """绘制表情强度图（柱状图）"""
        self.expression_bar_canvas.delete("all")
        
        width = self.expression_bar_canvas.winfo_width()
        height = self.expression_bar_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 获取当前表情数据
        expressions = self.last_expressions if self.last_expressions else {}
        
        if expressions:
            # 绘制柱状图
            margin = 50
            chart_width = width - 2 * margin
            chart_height = height - 2 * margin - 20
            
            exp_names = ['eyebrow_raise', 'eye_close', 'nose_scrunch', 'smile', 'lip_pucker']
            exp_labels = ['抬眉', '闭眼', '皱鼻', '微笑', '撅嘴']
            colors = ['red', 'blue', 'green', 'orange', 'purple']
            
            bar_width = chart_width / len(exp_names) * 0.8
            spacing = chart_width / len(exp_names) * 0.2
            
            for i, (exp_name, exp_label) in enumerate(zip(exp_names, exp_labels)):
                value = expressions.get(exp_name, 0)
                x1 = margin + i * (bar_width + spacing)
                x2 = x1 + bar_width
                y1 = margin + chart_height
                y2 = margin + chart_height - value * chart_height
                
                # 绘制柱子
                self.expression_bar_canvas.create_rectangle(x1, y1, x2, y2, fill=colors[i])
                
                # 绘制标签
                self.expression_bar_canvas.create_text((x1+x2)/2, y1 + 15, text=exp_label)
                self.expression_bar_canvas.create_text((x1+x2)/2, y2 - 10, text=f"{value:.2f}")
                
    def draw_expression_summary_chart(self):
        """绘制表情概要图（饼图）"""
        self.expression_summary_canvas.delete("all")
        
        width = self.expression_summary_canvas.winfo_width()
        height = self.expression_summary_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 表情分类
        categories = {
            "高兴": "happy",
            "悲伤": "sad",
            "惊喜": "surprise",
            "害怕": "fear",
            "愤怒": "angry",
            "其他": "others"
        }
        
        # 获取当前心情数据
        moods = self.mood_history[-1][1] if self.mood_history else {}
        
        if moods:
            # 计算各类别值
            values = []
            labels = []
            colors = ['yellow', 'blue', 'orange', 'purple', 'red', 'gray']
            
            total_others = 0
            for cat_name, cat_key in categories.items():
                if cat_key == "others":
                    values.append(total_others)
                elif cat_key in moods:
                    val = moods[cat_key]
                    values.append(val)
                    if cat_key not in ["happy", "sad", "surprise", "fear", "angry"]:
                        total_others += val
                else:
                    values.append(0)
                labels.append(cat_name)
                
            # 绘制饼图
            center_x = width // 2
            center_y = height // 2
            radius = min(width, height) // 3
            
            # 计算总值
            total = sum(values)
            if total == 0:
                total = 1  # 避免除零错误
                
            # 绘制扇形
            start_angle = 0
            for i, (value, label, color) in enumerate(zip(values, labels, colors)):
                if value > 0:
                    # 计算角度
                    angle = 360 * value / total
                    extent = angle
                    
                    # 绘制扇形
                    self.expression_summary_canvas.create_arc(
                        center_x - radius, center_y - radius,
                        center_x + radius, center_y + radius,
                        start=start_angle, extent=extent, fill=colors[i], outline="white")
                    
                    # 绘制标签
                    mid_angle = start_angle + angle / 2
                    label_x = center_x + (radius + 20) * np.cos(np.radians(mid_angle))
                    label_y = center_y - (radius + 20) * np.sin(np.radians(mid_angle))
                    self.expression_summary_canvas.create_text(label_x, label_y, text=f"{label}\n{value/total*100:.1f}%", font=("Arial", 8))
                    
                    start_angle += angle
                    
    def draw_mood_pie_chart(self):
        """绘制情绪绕线模型（饼图）- 4个选项"""
        self.mood_pie_canvas.delete("all")
        
        width = self.mood_pie_canvas.winfo_width()
        height = self.mood_pie_canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 情绪分类（按活跃/不活跃，愉快/不愉快）
        mood_categories = {
            "活跃愉快": ["happy", "surprise"],
            "活跃不愉快": ["angry", "fear"],
            "不活跃愉快": ["neutral"],
            "不活跃不愉快": ["sad", "disgust", "pain"]
        }
        
        # 获取当前心情数据
        moods = self.mood_history[-1][1] if self.mood_history else {}
        
        if moods:
            # 计算各类别值
            values = []
            labels = []
            colors = ['green', 'red', 'lightgreen', 'blue']
            
            for cat_name, cat_moods in mood_categories.items():
                val = sum([moods.get(mood, 0) for mood in cat_moods])
                values.append(val)
                labels.append(cat_name)
                
            # 绘制饼图
            center_x = width // 2
            center_y = height // 2
            radius = min(width, height) // 3
            
            # 计算总值
            total = sum(values)
            if total == 0:
                total = 1  # 避免除零错误
                
            # 绘制扇形
            start_angle = 0
            for i, (value, label, color) in enumerate(zip(values, labels, colors)):
                if value > 0:
                    # 计算角度
                    angle = 360 * value / total
                    extent = angle
                    
                    # 绘制扇形
                    self.mood_pie_canvas.create_arc(
                        center_x - radius, center_y - radius,
                        center_x + radius, center_y + radius,
                        start=start_angle, extent=extent, fill=colors[i], outline="white")
                    
                    # 绘制标签
                    mid_angle = start_angle + angle / 2
                    label_x = center_x + (radius + 20) * np.cos(np.radians(mid_angle))
                    label_y = center_y - (radius + 20) * np.sin(np.radians(mid_angle))
                    self.mood_pie_canvas.create_text(label_x, label_y, text=f"{label}\n{value/total*100:.1f}%", font=("Arial", 8))
                    
                    start_angle += angle
                    
    def estimate_mood(self, expressions):
        """根据表情估算心情（基于医疗面部表情分析标准）"""
        # 基于表情强度估算心情
        smile = expressions.get('smile', 0)
        eye_close = expressions.get('eye_close', 0)
        eyebrow_raise = expressions.get('eyebrow_raise', 0)
        nose_scrunch = expressions.get('nose_scrunch', 0)
        lip_pucker = expressions.get('lip_pucker', 0)
        
        # 快乐 (Happy) - 基于微笑和眼睛闭合（真诚的微笑会伴随眼部肌肉运动）
        happiness = min(1.0, smile * 0.7 + (1 - eye_close) * 0.3)
        
        # 悲伤 (Sad) - 嘴角下垂、眉毛内侧抬起、眼睛可能略微闭合
        sadness = min(1.0, max(0, (eyebrow_raise * 0.4 + (1 - smile) * 0.4 + eye_close * 0.2)))
        
        # 愤怒 (Angry) - 眉毛下降并靠拢、眼睛睁大、嘴唇紧闭或张开
        anger = min(1.0, max(0, (1 - eyebrow_raise) * 0.5 + (1 - eye_close) * 0.3 + (1 - smile) * 0.2))
        
        # 惊讶 (Surprise) - 眉毛大幅抬起、眼睛睁大、嘴巴张开
        surprise = min(1.0, eyebrow_raise * 0.6 + (1 - eye_close) * 0.4)
        
        # 恐惧 (Fear) - 眉毛抬起并靠拢、眼睛睁大、嘴巴张开或拉长
        fear = min(1.0, eyebrow_raise * 0.5 + (1 - eye_close) * 0.3 + (1 - smile) * 0.2)
        
        # 厌恶 (Disgust) - 眉毛略微下降、鼻子皱起、上唇抬起
        disgust = min(1.0, (1 - eyebrow_raise) * 0.3 + nose_scrunch * 0.4 + lip_pucker * 0.3)
        
        # 痛苦 (Pain) - 眉毛靠拢并下降、眼睛可能闭合、嘴巴可能张开或紧闭
        pain = min(1.0, (1 - eyebrow_raise) * 0.4 + eye_close * 0.3 + (1 - smile) * 0.3)
        
        # 平静 (Neutral) - 所有表情强度都较低
        neutral = max(0, 1 - (happiness + sadness + anger + surprise + fear + disgust + pain) / 7)
        
        # 心情强度数据
        moods = {
            'happy': happiness,
            'sad': sadness,
            'angry': anger,
            'surprise': surprise,
            'fear': fear,
            'disgust': disgust,
            'pain': pain,
            'neutral': neutral
        }
        
        # 找到最可能的心情
        dominant_mood = max(moods.items(), key=lambda x: x[1])
        mood_names = {
            'happy': '快乐',
            'sad': '悲伤',
            'angry': '愤怒',
            'surprise': '惊讶',
            'fear': '恐惧',
            'disgust': '厌恶',
            'pain': '痛苦',
            'neutral': '平静'
        }
        
        if dominant_mood[1] > 0.3:  # 阈值
            confidence = f"({dominant_mood[1]:.2f})"
            self.mood_label.config(text=f"当前心情: {mood_names.get(dominant_mood[0], '未知')} {confidence}")
        else:
            self.mood_label.config(text="当前心情: 平静")
            
    def estimate_medical_assessment(self, expressions):
        """根据表情估算医疗评估（增强版）"""
        smile = expressions.get('smile', 0)
        eye_close = expressions.get('eye_close', 0)
        eyebrow_raise = expressions.get('eyebrow_raise', 0)
        nose_scrunch = expressions.get('nose_scrunch', 0)
        lip_pucker = expressions.get('lip_pucker', 0)
        
        # 面部功能评估
        assessments = []
        
        # 眼部功能
        if eye_close > 0.6:
            assessments.append("眼轮匝肌功能正常")
        elif eye_close > 0.3:
            assessments.append("眼部肌肉活动轻度受限")
        else:
            assessments.append("眼部肌肉活动不足")
            
        # 额肌功能
        if eyebrow_raise > 0.6:
            assessments.append("额肌功能正常")
        elif eyebrow_raise > 0.3:
            assessments.append("额肌活动轻度受限")
        else:
            assessments.append("额肌活动不足")
            
        # 口周功能
        if smile > 0.6:
            assessments.append("口周肌群功能正常")
        elif smile > 0.3:
            assessments.append("口周肌肉活动轻度受限")
        else:
            assessments.append("口周肌肉活动不足")
            
        # 面部协调性
        coordination = 1.0 - abs(smile - eye_close)  # 简化的协调性评估
        if coordination > 0.8:
            assessments.append("面部肌肉协调性良好")
        elif coordination > 0.5:
            assessments.append("面部肌肉协调性一般")
        else:
            assessments.append("面部肌肉协调性较差")
            
        # 异常模式检测
        if nose_scrunch > 0.7 and lip_pucker > 0.7:
            assessments.append("可能存在面部痉挛")
        elif smile > 0.8 and eye_close < 0.2:
            assessments.append("可能存在面神经麻痹")
            
        # 综合评估
        if len([a for a in assessments if "正常" in a]) >= 3:
            overall = "面部运动功能基本正常"
        elif len([a for a in assessments if "受限" in a]) >= 2:
            overall = "面部运动功能轻度异常"
        elif len([a for a in assessments if "不足" in a]) >= 2:
            overall = "面部运动功能中度异常"
        else:
            overall = "需进一步临床评估"
            
        self.medical_label.config(text=f"医疗评估: {overall}")
        
    def update_video_display(self, imgtk):
        """更新视频显示"""
        # 清除画布上的所有内容
        self.video_canvas.delete("all")
        
        # 在画布中心显示图像
        canvas_width = self.video_canvas.winfo_width()
        canvas_height = self.video_canvas.winfo_height()
        
        # 如果画布尺寸无效，使用默认尺寸
        if canvas_width <= 1:
            canvas_width = 300
        if canvas_height <= 1:
            canvas_height = 225
            
        # 计算图像在画布上的居中位置
        img_width = imgtk.width()
        img_height = imgtk.height()
        x = (canvas_width - img_width) // 2
        y = (canvas_height - img_height) // 2
        
        # 在画布上创建图像
        self.video_canvas.create_image(x, y, anchor=tk.NW, image=imgtk)
        self.video_label.imgtk = imgtk  # 保持引用以防止被垃圾回收
        
    def on_closing(self):
        """窗口关闭事件处理"""
        self.stop_camera()
        self.window.destroy()

def main():
    root = tk.Tk()
    app = FacialExpressionGUI(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

if __name__ == "__main__":
    main()